package com.gonsin.gnews.service;

import com.gonsin.gnews.GNewsApplication;
import com.gonsin.gnews.NewsUtils;
import com.gonsin.gnews.annotations.GNewsEnvironment;
import com.gonsin.gnews.annotations.GNewsSender;
import com.gonsin.gnews.annotations.IBaseSender;
import com.gonsin.gnews.annotations.configuration.*;
import com.gonsin.gnews.annotations.enums.NewFieldType;
import com.gonsin.gnews.annotations.exceptions.GNewsException;
import com.gonsin.gnews.annotations.model.GNewsSenderDescriber;
import com.gonsin.gnews.annotations.model.ConfigValueModel;
import com.gonsin.gnews.annotations.services.IConfigService;
import com.gonsin.gnews.annotations.services.SenderRegister;
import com.gonsin.gnews.annotations.services.SenderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Primary;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 消息发送器注册
 */
@Service
@Slf4j
@Primary
public class NewsSenderRegisterImpl implements SenderRegister, InitializingBean, ApplicationContextAware, SenderService, IConfigService {

    private Map<String, GNewsSenderDescriber> senders = new HashMap<>();

    private Map<String, ConfigViewModel> configs = new HashMap<>();


    private ApplicationContext context;


    @Override
    public void afterPropertiesSet() throws Exception {



        NewsUtils.scannerPackage(GNewsApplication.getScanPackage(), new TypeFilter[]{
                new AnnotationTypeFilter(GNewsSender.class),
        }, clazz -> {
            if(!IBaseSender.class.isAssignableFrom(clazz)){
                throw new GNewsException("@GNewsSender必须批注在继承了IBaseSender的接口类上，请检查【%s】", clazz.getName());
            }

            GNewsSender sender = clazz.getAnnotation(GNewsSender.class);
            if(!sender.enable()){
                return;
            }


            GNewsSenderDescriber senderDescriber = new GNewsSenderDescriber(sender);

            // 构建Sender的实例对象
            IBaseSender sendComponent = null;
            try {
                sendComponent = (IBaseSender) context.getBean(clazz);
            } catch (Exception ex){
                if(GNewsEnvironment.LOG){
                    log.error(ex.toString(), ex);
                }
            }

            // 如果该发送器不是bean对象，则自行构建
            if(sendComponent == null){
                try {
                    sendComponent = (IBaseSender) clazz.newInstance();
                } catch (Exception e) {
                    log.error(e.toString(), e);
                    throw new GNewsException("构建【%s】实例失败，请至少添加一个无参的构造方法，或者添加@Component、@Service批注");
                }
            }

            registerSender(senderDescriber, sendComponent);
        });


        NewsUtils.scannerPackage(GNewsApplication.getScanPackage(), new TypeFilter[]{
                new AnnotationTypeFilter(GNewsConfiguration.class),
        }, clazz -> {

            GNewsConfiguration configuration = clazz.getAnnotation(GNewsConfiguration.class);

            // 构建Sender的实例对象
            String targetPlatform = configuration.targetPlatform();

            // 通过从数据库中获取配置信息
            registerConfig(targetPlatform, clazz);
        });
    }



    /**
     * 注册配置器
     */
    private void registerConfig(String targetPlatform, Class configClass) {

        GNewsSenderDescriber sender = senders.get(targetPlatform);
        if(sender == null){
            return;
        }
        String title = sender.getPlatformName();
        List<FieldItemModel> fieldList = parseConfigItem(configClass);
//        NewsConfigModel configModel = new NewsConfigModel();
//        configModel.setConfigInstance(configClass);
        ConfigViewModel viewModel = new ConfigViewModel();
        viewModel.setPlatformName(title);
        viewModel.setFieldItem(fieldList);
        viewModel.setConfigClass(configClass);


        configs.put(targetPlatform, viewModel);

    }

    /**
     * 解析
     * @param configClass
     * @return
     */
    private List<FieldItemModel> parseConfigItem(Class<?> configClass) {

        Field[] fields = configClass.getDeclaredFields();
        if(fields == null || fields.length == 0){
            return new ArrayList<>();
        }
        List<FieldItemModel> items = new ArrayList<>();

        for(Field f : fields){

            GNewsConfigValue value = f.getDeclaredAnnotation(GNewsConfigValue.class);
            if(value == null){
                continue;
            }
            FieldItemModel item = new FieldItemModel();
            item.setFieldName(f.getName());
            item.setLabelText(value.fieldText());
            item.setPlaceholder(value.placeholder());
            items.add(item);
            if(String.class.isAssignableFrom(f.getType())){
                item.setFieldType(NewFieldType.STRING.getValue());
            } else if(boolean.class.isAssignableFrom(f.getType()) || Boolean.class.isAssignableFrom(f.getType())){
                item.setFieldType(NewFieldType.BOOLEAN.getValue());
            } else if(int.class.isAssignableFrom(f.getType())
                    || Integer.class.isAssignableFrom(f.getType())
                    || Short.class.isAssignableFrom(f.getType())
                    || short.class.isAssignableFrom(f.getType())
                    || Long.class.isAssignableFrom(f.getType())
                    || long.class.isAssignableFrom(f.getType())
                    || Byte.class.isAssignableFrom(f.getType())
                    || byte.class.isAssignableFrom(f.getType())
            ) {
                item.setFieldType(NewFieldType.INT.getValue());
            } else if(Double.class.isAssignableFrom(f.getType())
                    || double.class.isAssignableFrom(f.getType())
                    || Float.class.isAssignableFrom(f.getType())
                    || float.class.isAssignableFrom(f.getType())) {
                item.setFieldType(NewFieldType.DOUBLE.getValue());
            } else {
                throw new GNewsException("不支持类型 %s", f.getType().getName());
            }
        }

        return items;
    }

    @Override
    public void registerSender(GNewsSenderDescriber senderDescriber, IBaseSender sender) {
        senderDescriber.setSender(sender);
        senders.put(senderDescriber.getPlatform(), senderDescriber);

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public GNewsSenderDescriber getSender(String platform) {
        return senders.get(platform);
    }

    @Override
    public List<GNewsSenderDescriber> findAll() {
        return senders.values().stream().sorted((s1, s2) -> s2.getOrder() - s1.getOrder()).collect(Collectors.toList());
    }

    @Override
    public ConfigViewModel getConfig(String platform) {
        return configs.get(platform);
    }

    @Override
    public List<ConfigViewModel> findAllConfigs() {
        List<GNewsSenderDescriber> senderDescribers = senders.values().stream().sorted((s1, s2) -> s2.getOrder() - s1.getOrder()).collect(Collectors.toList());
        List<ConfigViewModel> configModels = new ArrayList<>();
        for(GNewsSenderDescriber s : senderDescribers){
            ConfigViewModel viewModel = configs.get(s.getPlatform());
            configModels.add(viewModel.tClone());
        }
        return configModels;
    }
}
